namespace GYLite {
	export class AtlasRect extends egret.Rectangle implements GYLite.IPoolObject{
		/**创建虚拟图集区域
		 * @param x 区域左上角x
		 * @param y 区域左上角y
		 * @param w 区域宽度(包括间隙gap)
		 * @param h 区域高度(包括间隙gap)
		 * @param parent 父级区域
		 * @return AtlasRect区域
		*/
		public static createAtlasRect(x:number,y:number,w:number,h:number,parent:AtlasRect):AtlasRect
		{
			let r:AtlasRect;
			r = <AtlasRect>GYLite.PoolUtil.fromPool(AtlasRect);
			r.setTo(x,y,w,h);			
			if(parent)
			{
				r.$parent = parent;
				r.copyAtlasProp(parent);
			}			
			return r;
		}
		public static maxSize:number=1024;
		public static minSize:number=4;

		/**(公共变量)图集空白区域队列*/public emptyRects:Array<AtlasRect>;
		/**(公共变量)图集纹理区域队列*/public textureRects:Array<AtlasRect>;
		/**(公共变量)图集名称*/public atlasName:string;
		/**(公共变量)id*/public atlasId:number;
		/**小图之间的间隙*/public gap:number;
		/**是否图集Rect*/public isMain:boolean;

		public $childVec:Array<AtlasRect>;
		public $parent:AtlasRect;
		/**小图纹理名称*/public name:string;		
		/**纹理宽度，空白区域此属性为NaN*/
		public textureWidth:number;
		/**纹理高度，空白区域此属性为NaN*/
		public textureHeight:number;
		/**纹理区域的宽度、高度*/
		public atlasTexSize:number[];		
		/**纹理（指定的此区域的纹理）*/
		public texture:egret.Texture;

		/**去除透明像素的X偏移*/public offX:number;
		/**去除透明像素的Y偏移*/public offY:number;
		/**包括透明像素的原始宽度*/public sourceWidth:number;
		/**包括透明像素的原始高度*/public sourceHeight:number;
		/**图集管理器，只有主图集有管理器的引用*/private _mgr:AtlasManager;
		/**创建虚拟图集区域
		 * @param x 区域左上角x
		 * @param y 区域左上角y
		 * @param w 区域宽度
		 * @param h 区域高度
		 * @param mgr 图集管理器，只有主图集有管理器的引用		 
		*/
		public constructor(x:number=0,y:number=0,w:number=0,h:number=0,mgr:AtlasManager=null,isMain=false) {
			super(x,y,w,h);
			let s = this;
			s.gap = 0;
			s.$childVec = [];
			s._mgr = mgr;
			s.isMain = isMain;
			if(isMain)
			{
				s.emptyRects = [s];
				s.textureRects = [];
				s.atlasTexSize = [0,0];
			}				
			s.textureWidth = s.textureHeight = NaN;			
			s.atlasId = -1;
		}
		/**虚拟图集管理器*/
		public get mgr():AtlasManager
		{
			return this._mgr;
		}
		/**图集资源唯一键 图集名称+id(id其实也是唯一的)*/
		public get resKey():string
		{
			let s = this;
			if(s.atlasId > -1)
				return s.atlasName + "_" + s.atlasId;
			return s.atlasName;
		}
		/**添加空白区域*/
		public addEmptyRect(atlasRect:AtlasRect):void
		{
			let s= this;
			s.emptyRects.push(atlasRect);
			s.$childVec.push(atlasRect);
		}
		/**添加纹理区域*/
		public addTextureRect(atlasRect:AtlasRect):void
		{
			let s= this;
			s.textureRects[s.textureRects.length] = atlasRect;
			s.$childVec[s.$childVec.length] = atlasRect;
			s.atlasTexSize[0] = Math.max(atlasRect.x + atlasRect.width, s.atlasTexSize[0]);
			s.atlasTexSize[1] = Math.max(atlasRect.y + atlasRect.height, s.atlasTexSize[1]);
		}
		/**拷贝引用父级图集的内容(因此内容共享于最外层主图集)*/
		public copyAtlasProp(rect:AtlasRect):void
		{
			let s = this;
			s.emptyRects = rect.emptyRects;
			s.textureRects = rect.textureRects;
			s.atlasName = rect.atlasName;
			s.atlasId = rect.atlasId;
			s.atlasTexSize = rect.atlasTexSize;			
		}
		public findTextureRect(texName:string):AtlasRect
		{
			let len:number;
			let s =this;
			len = s.textureRects.length;
			while(--len > -1)
			{
				if(s.textureRects[len].name == texName)
					return s.textureRects[len];
			}
			return null;
		}
		/**裁切出指定宽高的空白区域
		 * @param w 宽度（不包括间隙）
		 * @param h 高度（不包括间隙）
		 * @param type 裁剪策略 排列方式0 偏向正方形 1 优先横向 2 优先纵向
		 * @param gap 间隙
		 * @return 空间不足返回null，裁切成功返回AtlasRect（其中宽高是包括间隙的）
		*/
		public cutRect(w:number,h:number,type:number=AtlasPlaceType.SQUARE,gap:number=1):AtlasRect
		{let s = this;
			var ind:number,len:number;
			var rect:AtlasRect;
			let sW:number,sH:number,leftMin:number,topMin:number;
			let suitableRect:AtlasRect;
			let minCp:number,min:number;
			let placeValue:number;
			topMin = leftMin = min = Number.MAX_VALUE;						
			sW = w + gap*2;
			sH = h + gap*2;
			len = s.emptyRects.length;
			while(--len > -1)
			{
				rect = s.emptyRects[len];				
				if(rect.width >= sW && rect.height >= sH)
				{
					placeValue = 0;					
					if(suitableRect)//先比较排列方式最适合的
					{
						if(type == AtlasPlaceType.SQUARE)						
							placeValue = (rect.x*rect.x + rect.y*rect.y)/(leftMin*leftMin + topMin*topMin);
						else if(type == AtlasPlaceType.HORICAL)						
							placeValue = rect.x / leftMin;
						else						
							placeValue = rect.y / topMin;
					}					
					minCp = (rect.width - sW)+(rect.height - sH);
					if(placeValue < 1 && placeValue <= minCp / min)//位置比小于1，且位置比小于空间比，认为更适合，因为单纯比较空间比，可能排列位置会相差很多
					{
						suitableRect = rect;
						leftMin = suitableRect.x;
						topMin = suitableRect.y;
						min = minCp;
						ind = len;
					}						
				}
			}
			if(suitableRect)
				return suitableRect.getClipRect(w,h,ind,type,gap)
			return null;
		}
		private getClipRect(w:number,h:number,ind:number,type:number=AtlasPlaceType.SQUARE,gap:number=1):AtlasRect
		{let s = this;
			var r:AtlasRect;
			var child1:AtlasRect,child2:AtlasRect;
			var leftW:number,leftH:number;
			let sW:number,sH:number;	
			let min:number = AtlasRect.minSize + gap*2;		
			sW = w + gap*2;
			sH = h + gap*2;			
			r = AtlasRect.createAtlasRect(s.x,s.y,sW,sH,s);
			r.gap = gap;			
			r.sourceWidth = r.textureWidth = w;
			r.sourceHeight = r.textureHeight = h;
			
			leftW = s.width - sW;
			leftH = s.height - sH;
			if(type == AtlasPlaceType.HORICAL || type == AtlasPlaceType.SQUARE && leftW < leftH)
			{
				if(leftW > min)
				{
					child1 = AtlasRect.createAtlasRect(r.right,r.y,leftW,sH,s);					
				}
				if(leftH > min)
				{
					child2 = AtlasRect.createAtlasRect(r.x,r.bottom,s.width,leftH,s);					
				}
			}
			else
			{
				if(leftH > min)
				{
					child1 = AtlasRect.createAtlasRect(r.x,r.bottom,sW, leftH,s);					
				}
				if(leftW > min)
				{
					child2 = AtlasRect.createAtlasRect(r.right,r.y,leftW, s.height, s);					
				}
			}
			
			s.textureRects[s.textureRects.length] = r;
			s.atlasTexSize[0] = Math.max(r.x + r.width, s.atlasTexSize[0]);
			s.atlasTexSize[1] = Math.max(r.y + r.height, s.atlasTexSize[1]);
			s.$childVec[s.$childVec.length] = r;
			if(child1)
			{				
				s.$childVec[s.$childVec.length] = child1;
				s.emptyRects[ind] = child1;
			}
			if(child2)
			{				
				s.$childVec[s.$childVec.length] = child2;
				if(child1 == null)
					s.emptyRects[ind] = child2;
				else
					s.emptyRects[s.emptyRects.length] = child2;
			}
			if(child1 == null && child2 == null)
				s.emptyRects.splice(ind,1);
			return r;
		}
		/**请求回到空白队列*/
		public backToEmpty():AtlasRect
		{let s = this;
			var len:number;
			var vec:Array<AtlasRect>;
			var c1:AtlasRect,c2:AtlasRect;
			var p:AtlasRect = s.$parent;
			if(p == null || p.$childVec.length > 2)//没有父级或者父级的子对象超过2个则认为是不需要往上递归恢复
			{
				s.name = null;
				var ind:number = s.textureRects.indexOf(s);
				if(ind > -1)
					s.textureRects.splice(ind,1);
				s.emptyRects.push(s);
				return s;
			}
			vec = p.$childVec;
			len = vec.length;
			while(--len>-1)
			{
				if(s == vec[len])continue;
				if(vec[len].isUse())
				{
					s.name = null;
					var ind:number = s.textureRects.indexOf(s);
					if(ind > -1)
						s.textureRects.splice(ind, 1);
					s.emptyRects.push(s);
					return s;
				}			
				if(c1==null)
					c1 = vec[len];
				else
					c2 = vec[len];
				
			}
			c1 && c1.clear();
			c2 && c2.clear();
			s.clear();
			vec.length = 0;
			return p.backToEmpty();
		}
		/**图集尺寸最小化*/
		public atlasMinResize():void
		{
			let len:number;
			let s = this;
			let atlasRect:AtlasRect;
			if(!s.isMain)
				return;			
			len = s.emptyRects.length;
			while(--len>-1)
			{
				atlasRect = s.emptyRects[len];
				if(s.atlasTexSize[0] < atlasRect.x + s.gap + 4 || s.atlasTexSize[1] < atlasRect.y + s.gap + 4)
				{
					s.emptyRects.splice(len, 1);
					continue;
				}
				if(s.atlasTexSize[0] < atlasRect.x + atlasRect.width)
					atlasRect.width = s.atlasTexSize[0] - atlasRect.x;
				if(s.atlasTexSize[1] < atlasRect.y + atlasRect.height)
					atlasRect.height = s.atlasTexSize[1] - atlasRect.y;
			}
			s.width = s.atlasTexSize[0];
			s.height = s.atlasTexSize[1];
		}
		
		/**是否已经填充图集*/
		public isTexture():boolean
		{
			return this.name != null;
		}
		/**是否已经被裁切使用*/
		public isUse():Boolean
		{let s = this;
			return s.$childVec.length > 0 || s.name != null;
		}
		/**输出图集配置*/
		public outputJson():any
		{
			let s = this;
			let obj:any = {};
			obj.name = s.name;
			obj.altasId = s.atlasId;
			obj.atlasName = s.atlasName;
			obj.gap = s.gap;
			obj.textureWidth = s.textureWidth;
			obj.textureHeight = s.textureHeight;
			obj.x = s.x;
			obj.y = s.y;
			obj.width = s.width;
			obj.height = s.height;
			obj.offX = s.offX;
			obj.offY = s.offY;
			obj.sourceWidth = s.sourceWidth;
			obj.sourceHeight = s.sourceHeight;
			obj.childVec = [];
			let i:number,len:number;
			len = s.$childVec.length;
			for(i=0;i<len;++i)
			{
				obj.childVec[obj.childVec.length] = s.$childVec[i].outputJson();				
			}
			return obj;
		}
		/**输入图集配置*/
		public inputJson(obj:any):void
		{
			let s = this;
			let rect:AtlasRect;
			s.name = obj.name;
			s.atlasId = obj.altasId;
			s.atlasName = obj.atlasName;
			s.gap = obj.gap;
			s.textureWidth = obj.textureWidth;
			s.textureHeight = obj.textureHeight;
			s.x = obj.x;
			s.y = obj.y;
			s.width = obj.width;
			s.height = obj.height;	
			s.offX = obj.offX;
			s.offY = obj.offY;
			s.sourceWidth = obj.sourceWidth;
			s.sourceHeight = obj.sourceHeight;		
			let i:number,len:number;
			len = obj.childVec.length;
			for(i=0;i<len;++i)
			{
				rect = <AtlasRect>GYLite.PoolUtil.fromPool(AtlasRect);
				rect.$parent = s;
				rect.copyAtlasProp(s);
				rect.inputJson(obj.childVec[i]);
				s.$childVec[s.$childVec.length] = rect;				
			}
			if(s.name)
			{
				s.textureRects[s.textureRects.length] = s;
				s.atlasTexSize[0] = Math.max(s.x + s.width, s.atlasTexSize[0]);
				s.atlasTexSize[1] = Math.max(s.y + s.height, s.atlasTexSize[1]);
			}				
			else if(!s.isUse())
				s.emptyRects[s.emptyRects.length] = s;
		}
		public createJsonSheet():void
		{
			let x:number,y:number,w:number,h:number,i:number,len:number;
			let json:any;
			let s = this;
			let arr:AtlasRect[];
			let rect:AtlasRect;
			json = {"file":s.resKey,"frames":{}};
			arr = s.textureRects;
			len = arr.length;				
			for(i=0;i<len;++i)
			{	
				rect = arr[i];
				x = s.x + s.gap;
				y = s.y + s.gap;
				w = s.textureWidth;
				h = s.textureHeight;
				json.frames[s.name] = {"x":x,"y":y,"w":w,"h":h,"offX":s.offX,"offY":s.offY,"sourceW":s.sourceWidth,"sourceH":s.sourceHeight};
			}
		}

		/**输出图集数据文件
		 * @param show 是否显示到body上
		 * @param callBack 完成回调 (result:IOutputAtlasResult[]):void=>{}
		 * @param thisObj this指向		 
		*/
		public static outputAtlas(atlasRects:AtlasRect[],show:boolean=false,callBack:Function,thisObj:any, result:IOutputAtlasResult[]=null):void
		{			
			let json:any;
			let j:number,len2:number;
			let arr:AtlasRect[];
			let x:number,y:number,w:number,h:number;
			let resObj:ResObject;
			let rect:AtlasRect,atlasRect:AtlasRect;		
            
			if(atlasRects == null || atlasRects.length == 0)
			{
				if(callBack!=null)
					callBack.call(thisObj);
				return;
			}

			if(result == null)
				result = [];
			let cvs:HTMLCanvasElement = document.createElement("canvas");
			let ctx:CanvasRenderingContext2D  = cvs.getContext("2d");
			let res:egret.Texture;		 
			atlasRect= atlasRects[result.length];
			cvs.width = atlasRect.width;
			cvs.height = atlasRect.height;
			if(cvs.parentNode == null && show)
			{
				cvs.style.position = "absolute";
				cvs.style.left = "300px";
				cvs.style.pointerEvents = "none";
				document.body.appendChild(cvs);
			}			
			arr = atlasRect.textureRects;
			json = {"file":atlasRect.resKey,"frames":{}};
			ctx.clearRect(0,0,cvs.width,cvs.height);
			len2 = arr.length;
			for(j=0;j<len2;++j)
			{
				rect = arr[j];
				if(rect.texture)
					res = rect.texture;
				else
				{
					resObj = GYLite.GYLoader.getRes(rect.name,rect.resKey);				
					if(resObj)
						res = resObj.res;
					else
					{
						console.warn("查询到空图集区域!"+rect.name+"-"+rect.atlasName);
						rect.backToEmpty();
						continue;
					}
				}				
				x = rect.x + rect.gap;
				y = rect.y + rect.gap;
				w = rect.textureWidth;
				h = rect.textureHeight;
				json.frames[rect.name] = {"x":x,"y":y,"w":w,"h":h,"offX":rect.offX,"offY":rect.offY,"sourceW":rect.sourceWidth,"sourceH":rect.sourceHeight};
				
				ctx.drawImage(res.bitmapData.source,0,0,rect.sourceWidth,rect.sourceHeight,x - rect.offX,y - rect.offY,rect.sourceWidth,rect.sourceHeight);
			}				
			
			cvs.toBlob((blob)=>{
				result[result.length] = {json:json,blob:blob};
				if(result.length == atlasRects.length)
					callBack.call(thisObj,result);
				else
					AtlasRect.outputAtlas(atlasRects, show, callBack, thisObj, result);
			},"image/png");
					
		}

		public inPool:boolean;
		public disposed:boolean;
		protected beforeToPool():void
		{
			let s= this;
			var ind:number = s.emptyRects.indexOf(this);
			if(ind > -1)
				s.emptyRects.splice(ind,1);
			var ind:number = s.textureRects.indexOf(this);
			if(ind > -1)
				s.textureRects.splice(ind,1);
			s.$parent = null;
			s.atlasName = s.name = null;
			s.atlasId = -1;
			s.gap = 0;
			s.textureWidth = s.textureHeight = NaN;
			s.atlasTexSize[0] = s.atlasTexSize[1] = 0;
			s.isMain = false;
			if(s._mgr)
			{
				s._mgr.removeAtlas(s);
				s._mgr = null;
			}
		}
		public clear(): void
		{
			let s = this;
			if(s.inPool)
				return;
			s.beforeToPool();
			GYLite.PoolUtil.toPool(this,AtlasRect);
		}		
        public outPoolInit(): void
		{

		}
        public dispose(): void
		{
			let s= this;
			s.beforeToPool();
		}
	}
	export interface IOutputAtlasResult
	{
		json:any;
		blob:Blob;
	}
}